package com.ithinks.flow.common.utils.protocol.modbus.mastertcp;

import com.digitalpetri.modbus.codec.Modbus;
import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.ReadCoilsRequest;
import com.digitalpetri.modbus.requests.ReadDiscreteInputsRequest;
import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;
import com.digitalpetri.modbus.requests.ReadInputRegistersRequest;
import com.digitalpetri.modbus.responses.ReadCoilsResponse;
import com.digitalpetri.modbus.responses.ReadDiscreteInputsResponse;
import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
import com.digitalpetri.modbus.responses.ReadInputRegistersResponse;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * @Description modbus TCP 协议通信：使用digitalpetri
 * @ClassName modbusMasterTcp
 * @Author MR.LI
 * @date 2020.12.07 14:49
 */
public class ModbusMasterTcp {

    static ModbusTcpMaster master;
    private int address;

    /**
     * 获取TCP协议的Master
     */
    public static void initModbusTcpMaster(){
        if(master == null){
            ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("localhost").setPort(502).build();
            master = new ModbusTcpMaster(config);
        }
    }

    /**
     * 释放资源
     */
    public static void release(){
        if(master != null){
            master.disconnect();
        }
        Modbus.releaseSharedResources();
    }

    /**
     * 读取coils开关量
     * @param address 寄存器开关地址
     * @param quantity 数量
     * @param united ID
     * @return
     */
    public static Boolean readCoils(int address,int quantity,int united){

        Boolean result = null;
        try {
            CompletableFuture<ReadCoilsResponse> future = master.sendRequest(new ReadCoilsRequest(address,quantity),united);
            ReadCoilsResponse readCoilsResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理
            if(readCoilsResponse != null){
                ByteBuf buf = readCoilsResponse.getCoilStatus();
                result = buf.readBoolean();
                ReferenceCountUtil.release(readCoilsResponse);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 读取readDiscreteInputs开关量
     * @param address 寄存器开始i地址
     * @param unitId  id
     * @param quantity 数量
     * @return
     */
    public static Boolean readDiscreteInputs(int address,int quantity,int unitId){
        Boolean result = null;
        CompletableFuture<ReadDiscreteInputsResponse> future = master.sendRequest(new ReadDiscreteInputsRequest(address, quantity),unitId);
        ReadDiscreteInputsResponse discreteInputsResponse = null;
        try {
            discreteInputsResponse = future.get();
            if (discreteInputsResponse != null){
                ByteBuf buf = discreteInputsResponse.getInputStatus();
                result = buf.readBoolean();
                ReferenceCountUtil.release(discreteInputsResponse);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 读取holdingRegister数据
     * @param address 寄存器地址
     * @param quantity 寄存器数量
     * @param unitId id
     * @return
     */
    public static Number readHoldingRegisters(int address,int quantity,int unitId){
        Number result = null;
        CompletableFuture<ReadHoldingRegistersResponse> future = master.sendRequest(new ReadHoldingRegistersRequest(address, quantity),unitId);
        ReadHoldingRegistersResponse readHoldingRegistersResponse = null;
        try {
            readHoldingRegistersResponse = future.get();
            if(readHoldingRegistersResponse !=null){
                ByteBuf buf = readHoldingRegistersResponse.getRegisters();
                result = buf.readFloat();
                ReferenceCountUtil.release(readHoldingRegistersResponse);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 读取InputRegisters模拟量数据
     *
     * @param address
     *            寄存器开始地址
     * @param quantity
     *            数量
     * @param unitId
     *            ID
     * @return 读取值
     * @throws InterruptedException
     *             异常
     * @throws ExecutionException
     *             异常
     */
    public static Number readInputRegisters(int address, int quantity, int unitId)
            throws InterruptedException, ExecutionException {
        Number result = null;
        CompletableFuture<ReadInputRegistersResponse> future = master
                .sendRequest(new ReadInputRegistersRequest(address, quantity), unitId);
        ReadInputRegistersResponse readInputRegistersResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理
        if (readInputRegistersResponse != null) {
            ByteBuf buf = readInputRegistersResponse.getRegisters();
            result = buf.readDouble();
            ReferenceCountUtil.release(readInputRegistersResponse);
        }
        return result;
    }

    public static void main(String[] args) {
        try {
            // 初始化资源
            initModbusTcpMaster();

            // 执行操作

            // 读取开关量
            System.out.println(readCoils(0, 1, 1));
            System.out.println(readDiscreteInputs(0, 1, 1));
            System.out.println(readDiscreteInputs(1, 1, 1));

            // 读取模拟量
            System.out.println(readHoldingRegisters(0, 2, 1));
            System.out.println(readHoldingRegisters(2, 2, 1));
            System.out.println(readHoldingRegisters(4, 2, 1));
            System.out.println(readInputRegisters(2, 4, 1));
            System.out.println(readInputRegisters(6, 4, 1));

            // 释放资源
            release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

